// Package redis 工具包
package redis

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/redis/go-redis/v9"

	"github.com/runningwater/gohub/pkg/logger"
)

// Client Redis 服务
type Client struct {
	Client  *redis.Client
	Context context.Context
}

// Ping 测试 Redis 连接
// 通过 Ping 方法测试 Redis 连接是否正常
func (rds *Client) Ping() error {
	_, err := rds.Client.Ping(rds.Context).Result()
	return err
}

// Set 设置 Redis 键值对, 并设置过期时间
func (rds *Client) Set(key string, value any, expiration time.Duration) bool {
	if err := rds.Client.Set(rds.Context, key, value, expiration).Err(); err != nil {
		logger.ErrorString("Redis", "Set", err.Error())
		return false
	}
	return true
}

// Get 获取 Redis 键值对
func (rds *Client) Get(key string) string {
	val, err := rds.Client.Get(rds.Context, key).Result()
	if err != nil {
		logger.ErrorString("Redis", "Get", err.Error())
		return ""
	}
	return val
}

// Has 检查 Redis 中是否存在某个键
func (rds *Client) Has(key string) bool {
	_, err := rds.Client.Get(rds.Context, key).Result()
	if err != nil {
		if errors.Is(err, redis.Nil) {
			logger.ErrorString("Redis", "Has", err.Error())
		}
		return false
	}
	return true
}

// Del 删除 Redis 中的键, 支持多个 key 传参
func (rds *Client) Del(keys ...string) bool {
	if err := rds.Client.Del(rds.Context, keys...).Err(); err != nil {
		logger.ErrorString("Redis", "Del", err.Error())
		return false
	}
	return true
}

// FlushDb 清空 Redis 数据库
func (rds *Client) FlushDb() bool {
	if err := rds.Client.FlushDB(rds.Context).Err(); err != nil {
		logger.ErrorString("Redis", "FlushDb", err.Error())
		return false
	}
	return true
}

// Increment 当参数只有 1 个时，增加 Redis 中的键值对增加 1
// 当参数有 2 个时，第一个参数为 Key，第二个参数为增加的值 int64 类型
func (rds *Client) Increment(args ...any) bool {
	switch len(args) {
	case 1:
		key := args[0].(string)
		if err := rds.Client.Incr(rds.Context, key).Err(); err != nil {
			logger.ErrorString("Redis", "Increment", err.Error())
			return false
		}
	case 2:
		key := args[0].(string)
		value := args[1].(int64)
		if err := rds.Client.IncrBy(rds.Context, key, value).Err(); err != nil {
			logger.ErrorString("Redis", "Increment", err.Error())
			return false
		}
	default:
		logger.ErrorString("Redis", "Increment", "参数错误, 只能传入 1 或 2 个参数")
		return false
	}
	return true
}

// Decrement 当参数只有 1 个时，减少 Redis 中的键值对减少 1
// 当参数有 2 个时，第一个参数为 Key，第二个参数为减少的值 int64 类型
func (rds *Client) Decrement(args ...any) bool {
	switch len(args) {
	case 1:
		key := args[0].(string)
		if err := rds.Client.Decr(rds.Context, key).Err(); err != nil {
			logger.ErrorString("Redis", "Decrement", err.Error())
			return false
		}
	case 2:
		key := args[0].(string)
		value := args[1].(int64)
		if err := rds.Client.DecrBy(rds.Context, key, value).Err(); err != nil {
			logger.ErrorString("Redis", "Decrement", err.Error())
			return false
		}
	default:
		logger.ErrorString("Redis", "Decrement", "参数错误, 只能传入 1 或 2 个参数")
		return false
	}
	return true
}

// func (rds *RedisClient) Reconnect(ctx context.Context) {
//   for {
// 	err := rds.Ping()
// 	if err == nil {
// 	  	logger.InfoString("Redis", "Ping", "Redis 连接正常")
// 		return
// 	}

// 	logger.ErrorString("Redis", "Ping", "Redis 连接异常，正在重连...")

// 	// 重新连接 Redis
// 	rds.Client = redis.NewClient(&redis.Options{
// 		Addr:     rds.Client.Options().Addr,
// 		Password: rds.Client.Options().Password,
// 		DB:       rds.Client.Options().DB,
// 	})

// 	select {
// 	case <-ctx.Done():
// 		logger.InfoString("Redis", "Reconnect", "Redis 重连已取消")
// 		return
// 		case <-time.After(5 * time.Second):

// 	}
//   }
// }

// once 确保全局的 Redis 对象只实例一次
var once sync.Once

// Redis 全局 Redis， 使用 db 1
var Redis *Client

// ConnectRedis 连接 redis 数据库，设置全局 Redis 对象
func ConnectRedis(address, username, password string, db int) {
	once.Do(func() {
		Redis = NewClient(address, username, password, db)
	})
}

func NewClient(address, username, password string, db int) *Client {

	rds := &Client{}
	rds.Context = context.Background()

	// 使用 redis 库里的 NewClient 初始化连接
	rds.Client = redis.NewClient(&redis.Options{
		Addr:     address,
		Password: password,
		DB:       db,
	})

	err := rds.Ping()
	logger.LogIf(err)

	// 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	//   defer cancel()
	//   rds.Reconnect(ctx)

	return rds
}
